#include "string.h"
#include "stdio.h"
#include "types.h"
#include "io.h"
#include "dma.h"
#include "unione_lite.h"
#include <rtthread.h>
#include "spi_flash_drv.h"
#include "DrvUART010.h"

int SPI_BASE_ADDR[2] = {SPI_FTSPI020_0_PA_BASE, SPI_FTSPI020_1_PA_BASE};

#define CPU_MODE 0
#define DMA_MODE 1

struct ftspi020_cmd cmd_global;
unsigned int select_chip;
unsigned int txfifo_depth, rxfifo_depth;
unsigned int flash_read;
unsigned char read_opcode;
unsigned char nor_id[4];
static rt_sem_t flash_rw_sem = RT_NULL;


#define DEBUG_PRINT(format, ...)	 	fLib_printf("%s %d:"format, __FUNCTION__, __LINE__, ##__VA_ARGS__)

static int ftspi020_txfifo_ready(SPI_REG *base)
{
	return base->SR & FTSPI020_STS_TFR;
}

static int ftspi020_rxfifo_ready(SPI_REG *base)
{
	return base->SR & FTSPI020_STS_RFR;
}

static void ftspi020_write_word(SPI_REG *base, const void *data, int wsize)
{
	if (data) {
		switch (wsize) {
		case 1:
			base->DR = *(const u8  *)data;
			break;

		case 2:
			base->DR = *(const u16  *)data;
			break;

		default:
			base->DR = *(const u32  *)data;
			break;
		}
	}
}

static void ftspi020_read_word(SPI_REG *base, void *buf, int wsize)
{
	if (buf) {
		switch (wsize) {
		case 1:
			*(u8 *) buf = base->DR;
			break;

		case 2:
			*(u16 *) buf = base->DR;
			break;

		default:
			*(u32 *) buf = base->DR;
			break;
		}
	}
}

static int ftspi020_txrx(SPI_REG *base, const u8 *tx_buf,
                         u8 *rx_buf, int len, int bpw)
{
	int wsize;

	if (bpw <= 8)
		wsize = 1;
	else if (bpw <= 16)
		wsize = 2;
	else
		wsize = 4;

	while (len > 0) {
		int access_byte;

		if (tx_buf) {
			access_byte = min_t(len, txfifo_depth);
			len -= access_byte;

			while (!ftspi020_txfifo_ready(base))
				;

			while (access_byte) {
				ftspi020_write_word(base, tx_buf, wsize);

				tx_buf += wsize;
				access_byte -= wsize;
			}
		} else if (rx_buf) {
			while (!ftspi020_rxfifo_ready(base))
				;

			access_byte = min_t(len, rxfifo_depth);
			len -= access_byte;

			while (access_byte) {
				ftspi020_read_word(base, rx_buf, wsize);

				rx_buf += wsize;
				access_byte -= wsize;
			}
		}
	}

	return len;
}

static int ftspi020_wait_complete(SPI_REG *base, signed long timeout)
{
	u32 isr;

	do {
		isr = base->ISR;
		base->ISR = isr;

		if (isr & FTSPI020_ISR_CMD_CMPL)
			goto out;

		timeout -= 1;
	} while (timeout > 0);

out:
	return timeout < 0 ? 0 : timeout;
}

void ftspi020_reset_ctrl(SPI_REG * base)
{
	int val;

	/* Set DAMR port selection to Command based */
	val = base->CR;
	val |= FTSPI020_CTRL_ABORT;
	base->CR = val;
}

int ftspi020_firecmd(SPI_REG *base, struct ftspi020_cmd *cmd, const u8 *tx_buf, u8 *rx_buf, int bpw)
{
	int len = cmd->cq2;
	int ret;

	base->CMD0 = cmd->cq0;
	base->CMD1 = cmd->cq1;
	base->CMD2 = cmd->cq2;
	base->CMD3 = cmd->cq3;
	if (tx_buf || rx_buf) {
		ret = ftspi020_txrx(base, tx_buf, rx_buf, len, bpw);
		if (ret)
			goto out;
	}
	ret = ftspi020_wait_complete(base, 1000000);
	if (!ret) {
		/* debug

		sprintf(uart_buf, "ftspi020_nand: timeout: cmd0 0x%08x cmd1 0x%08x " \
		       "cmd2 0x%08x cmd3 0x%08x\r\n",
		       base->CMD0,
		       base->CMD1,
		       base->CMD2,
		       base->CMD3);
		Uart_Puts(uart_buf);
		       */
		ret = -ETIMEDOUT;
		goto out;
	}
	return 0;
out:
	ftspi020_reset_ctrl(base);
	return ret;
}

static int ftspi020_nor_get_status(SPI_REG *base, STATUS_REGS addr, u8 *val)
{
    int ret;
	int len = 3; // bug fix, harris 20190701
	unsigned int op;
	struct ftspi020_cmd* cmd = &cmd_global;
	
	switch(addr)
	{
		case STATUS_0: op = CMD_READ_STATUS0; break;
		case STATUS_1: op = CMD_READ_STATUS1; break;
		case STATUS_2: op = CMD_READ_STATUS2; break;
		default: op = CMD_READ_STATUS0;
	}

	cmd->cq0 = 0;
	cmd->cq1 = FTSPI020_CMD1_OP_CODE_1_BYTE |
	           FTSPI020_CMD1_ADDR_BYTES(0);
	cmd->cq2 = len;
	cmd->cq3 = FTSPI020_CMD3_INSTR_CODE(op)  |
	           FTSPI020_CMD3_CE(select_chip) |
	           FTSPI020_CMD3_READ  |
	           FTSPI020_CMD3_INTR_EN;

	ret = ftspi020_firecmd(base, cmd, NULL, val, 8);
	if(ret) {
		DEBUG_PRINT("ftspi020_nor: error %d getting status\r\n", (int)ret);
		return ret;
	}

	return 0;
}

static int ftspi020_nor_write_enable(SPI_REG *base)
{
	struct ftspi020_cmd *cmd = &cmd_global;
	unsigned long deadline;
	u8  val;
	int timeout = 0, ret = -ETIMEDOUT;

	cmd->cq0 = 0;
	cmd->cq1 = FTSPI020_CMD1_OP_CODE_1_BYTE;
	cmd->cq2 = 0;
	cmd->cq3 = FTSPI020_CMD3_INSTR_CODE(CMD_WRITE_ENABLE) |
	           FTSPI020_CMD3_CE(select_chip) |
	           FTSPI020_CMD3_WRITE |
	           FTSPI020_CMD3_INTR_EN;
	ret = ftspi020_firecmd(base, cmd, NULL, NULL, 8);
	if (ret) {
		DEBUG_PRINT("ftspi020_nor: error %d writing enable\r\n", (int)ret);

		ret = -ETIMEDOUT;
		return ret;
	}

	deadline = 1000000;

	do {
		if (deadline-- < 0)
			timeout = 1;

		if ((ret = ftspi020_nor_get_status(base, STATUS_0, &val)) < 0) {
			DEBUG_PRINT("ftspi020_nor: error %d reading wel status\r\n", ret);
			return ret;
		} else if(val & SPI_NOR_FEATURE_STATUS_WEL)
			return 0;

	} while (!timeout);

	DEBUG_PRINT("ftspi020_nor: ftspi020_write_enable timeout\r\n");
	return ret;
}

static int ftspi020_nor_wait_till_ready(SPI_REG *base)
{
	u8 val = 0;
	int timeout = 0, ret = 0;
	
    timeout = 1000000;
	do{
		timeout--;
		
		if ((ret = ftspi020_nor_get_status(base, STATUS_0, &val)) < 0) {
			fLib_printf("ftspi020_nor: error %d waitting ready\r\n", ret);
			return ret;
		}
		if (!(val & SPI_NOR_FEATURE_STATUS_OIP)) {
			if(!(val & SPI_NOR_FEATURE_STATUS_WEL)){ //bug fix for silent program/erase error
			    ret = 0;
			    break;
			}
		}
	} while(timeout);

	if((0 == timeout) && (0 == ret)) {
		ret = -ETIMEDOUT;
		DEBUG_PRINT("ftspi020_nor: ftspi020_nor_wait_till_ready timeout\r\n");
	}

	return ret;
}

static int ftspi020_nor_set_status(SPI_REG *base, STATUS_REGS addr, u8 *val)
{
	struct ftspi020_cmd* cmd = &cmd_global;
	int len = 1, ret;
	unsigned int op = 0;

	ftspi020_nor_write_enable(base);

	switch(addr)
	{
		case STATUS_0: op = CMD_WRITE_STATUS_0;  break;
		case STATUS_1: op = CMD_WRITE_STATUS_1;  break;
		case STATUS_2: op = CMD_WRITE_STATUS_2;  break;
		default: 
			DEBUG_PRINT("illegal status reg address: %d!\r\n", addr);
	}

	cmd->cq0 = 0;
	cmd->cq1 = FTSPI020_CMD1_OP_CODE_1_BYTE |
			   FTSPI020_CMD1_ADDR_BYTES(0);
	cmd->cq2 = len;
	cmd->cq3 = FTSPI020_CMD3_INSTR_CODE(op) |
			   FTSPI020_CMD3_CE(select_chip) |
			   FTSPI020_CMD3_WRITE |
			   FTSPI020_CMD3_INTR_EN;
	ret = ftspi020_firecmd(base, cmd, val, NULL, 8);

	if(ret) {
		DEBUG_PRINT("ftspi020_nor: error %d setting feature\r\n", (int)ret);
		return ret;
	}

	if(ftspi020_nor_wait_till_ready(base)){
		DEBUG_PRINT("ftspi020_nor: set_status wait failed...\r\n");
		return -1;
	}
	return 0;
}

int ftspi020_nor_set_quad_mode(SPI_REG *base)
{
	u8  val;
	int ret;

	//DEBUG_PRINT("ftspi020_nor: ftspi020_nor_set_quad_mode\r\n");

	ret = ftspi020_nor_get_status(base, STATUS_1, &val);
	if (ret) {
		DEBUG_PRINT("ftspi020_nor: error %d reading status S8~S15\r\n", ret);
		return ret;
	}
	if (!(val & SPI_NOR_FEATURE_OTP_QUAD_ENABLE)) {

		val |= SPI_NOR_FEATURE_OTP_QUAD_ENABLE;
		ret = ftspi020_nor_set_status(base, STATUS_1, &val);
		if (ret) {
			DEBUG_PRINT("error %d enabling quad\r\n", ret);
			return -EIO;
		}

		ftspi020_nor_get_status(base, STATUS_1, &val);
		if (!(val & SPI_NOR_FEATURE_OTP_QUAD_ENABLE)) {
			DEBUG_PRINT("flash still not enabling quad(0x%x)...\r\n", val);
			return -EBUSY;
		}
	}
    
	return 0;
}

int ftspi020_nor_set_non_quad_mode(SPI_REG *base)
{
	u8  val;
	int ret;

	DEBUG_PRINT("ftspi020_nor: ftspi020_nor_set_non_quad_mode\r\n");

	ret = ftspi020_nor_get_status(base, STATUS_1, &val);
	if (ret) {
		DEBUG_PRINT("ftspi020_nor: error %d reading status S8~S15\r\n", ret);
		return ret;
	}
	if ((val & SPI_NOR_FEATURE_OTP_QUAD_ENABLE)) {

		val &= ~SPI_NOR_FEATURE_OTP_QUAD_ENABLE;
		ret = ftspi020_nor_set_status(base, STATUS_1, &val);
		if (ret) {
			DEBUG_PRINT("error %d disabling quad\r\n", ret);
			return -EIO;
		}

		ftspi020_nor_get_status(base, STATUS_1, &val);
		if ((val & SPI_NOR_FEATURE_OTP_QUAD_ENABLE)) {
			DEBUG_PRINT("flash still enabling quad(0x%x)...\r\n", val);
			return -EBUSY;
		}
	}

	return 0;
}

static int ftspi020_nor_unlock(SPI_REG *base)
{
	u8  val;
	int ret;
    
	ret = ftspi020_nor_get_status(base, STATUS_0, &val);
	if (ret) {
		DEBUG_PRINT("ftspi020_nor: error %d reading lock status\r\n", ret);
		return ret;
	}
	if(val & SPI_NOR_PROTECTION_BP0TO4) {
		val &= ~SPI_NOR_PROTECTION_BP0TO4;
		ret = ftspi020_nor_set_status(base, STATUS_0, &val);
		if (ret) {
			DEBUG_PRINT("ftspi020_nor: error %d unlocking flash\r\n", ret);
			return ret;
		}
		ftspi020_nor_get_status(base, STATUS_0, &val);
		if(val & SPI_NOR_PROTECTION_BP0TO4) {
			DEBUG_PRINT("ftspi020_nor: flash still locking(%d)...\r\n", val);
			return -EBUSY;
		}
	}
	return 0;
}

int ftspi020_nor_hw_setup(SPI_REG *base)
{
	unsigned int val;
	unsigned int div;

	ftspi020_reset_ctrl(base);

	/* Calculate SPI clock divider */
	div = 2; // 2 ~ 8

	/* Set SPI clock divider and DAMR port selection to Command based */
	val = base->CR;
	val &= ~FTSPI020_CTRL_DAMR_PORT;
	val &= FTSPI020_CTRL_CLK_DIVIDER_MASK;
	val |= ((div >> 1) - 1);
	base->CR = val;

	/* busy bit location */
	val = base->CR;
	val &= FTSPI020_CTRL_READY_LOC_MASK;
	val |= FTSPI020_CTRL_READY_LOC(0);
	base->CR = val;
	base->ACTR = 0x31;// 0x3F -> 0x31

	rxfifo_depth = FTSPI020_FEATURE_RXFIFO_DEPTH(base->FEATURE);
	txfifo_depth = FTSPI020_FEATURE_TXFIFO_DEPTH(base->FEATURE);

	select_chip = 0;
	
	flash_read = SPI_NOR_QUAD; //SPI_NOR_DUAL

	if(flash_read == SPI_NOR_QUAD)
	{
		ftspi020_nor_set_quad_mode(base);
	}
	else
	{
		ftspi020_nor_set_non_quad_mode(base);
	}

	switch (flash_read) {
		case SPI_NOR_QUAD:
			read_opcode = CMD_READ_QUAD_OUTPUT_FAST;
			break;
		case SPI_NOR_DUAL:
			read_opcode = CMD_READ_DUAL_OUTPUT_FAST;
			break;
		case SPI_NOR_FAST:
			read_opcode = CMD_READ_ARRAY_FAST;
			break;
		case SPI_NOR_NORMAL:
			read_opcode = CMD_READ_ARRAY_SLOW;
			break;
		default:

		DEBUG_PRINT("ftspi020_nor: No Read opcode defined\r\n");
		goto bni_err;
	}

	ftspi020_nor_unlock(base);

	return 0;

bni_err:
	return -EINVAL;
}

void Check_NorFlashStatus(SPI_REG *base)
{
	u8 STATUS_012[3];

	int ret;
	ret = ftspi020_nor_get_status(base, STATUS_0, STATUS_012);
	if (ret) {
		DEBUG_PRINT("ftspi020_nor: get SPI_NAND_FEATURE_PROTECTION failed...\r\n");
	}
	ret = ftspi020_nor_get_status(base, STATUS_1, STATUS_012 + 1);
	if (ret) {
		DEBUG_PRINT("ftspi020_nor: get SPI_NAND_FEATURE_OTP failed...\r\n");
	}
	ret = ftspi020_nor_get_status(base, STATUS_2, STATUS_012 + 2);
	if (ret) {
		DEBUG_PRINT("ftspi020_nor: get SPI_NAND_FEATURE_STATUS failed...\r\n");
	}
	DEBUG_PRINT("Nor Flash Feature: 0x%X, 0x%X, 0x%X\r\n", STATUS_012[0], STATUS_012[1], STATUS_012[2]);
}

static inline unsigned char ftspi020_nor_tx_nbits(void)
{
	switch (flash_read) {
	case SPI_NOR_QUAD:
		return FTSPI020_CMD3_QUAD_MODE;
	default:
		return FTSPI020_CMD3_SERIAL_MODE;
	}
}

static inline unsigned int ftspi020_nor_rx_nbits(void)
{
	switch (flash_read) {
	case SPI_NOR_DUAL:
		return FTSPI020_CMD3_DUAL_MODE;
	case SPI_NOR_QUAD:
		return FTSPI020_CMD3_QUAD_MODE;
	default:
		return FTSPI020_CMD3_SERIAL_MODE;
	}
}

static inline unsigned int ftspi020_nor_rx_dummy(void)
{
	switch (flash_read) {
	case SPI_NOR_NORMAL:
		return 0;
	default:
		return 8;
	}
}

static int ftspi020_nor_read_data_bytes(SPI_REG *base, int addr, int len, unsigned char *buf)
{
	struct ftspi020_cmd *cmd = &cmd_global;
	int ret;

	//DEBUG_PRINT("ftspi020_nor: ftspi020_nor_read_data_bytes addr = 0x%x\r\n", addr);

	cmd->cq0 = addr;
	cmd->cq1 = FTSPI020_CMD1_OP_CODE_1_BYTE |
	           FTSPI020_CMD1_DUMMY_CYCLE(ftspi020_nor_rx_dummy()) |
	           FTSPI020_CMD1_ADDR_BYTES(3);
	cmd->cq2 = len;
	cmd->cq3 = FTSPI020_CMD3_INSTR_CODE(read_opcode) |
	           FTSPI020_CMD3_CE(select_chip) |
	           ftspi020_nor_rx_nbits() |
	           FTSPI020_CMD3_READ |
	           FTSPI020_CMD3_INTR_EN;

	ret = ftspi020_firecmd(base, cmd, NULL, buf, 32);
	if(ret) {
		DEBUG_PRINT("ftspi020_nor: error %d reading data bytes\r\n", (int)ret);
		return ret;
	}

	return 0;
}

static int ftspi020_nor_read_buf(SPI_REG *base, u8 *buf, int len, int addr)
{
	if(ftspi020_nor_read_data_bytes(base, addr, len, buf)){
		DEBUG_PRINT("ftspi020_nand: read_buf read failed...\r\n");
		return -1;
	}

	return 0;
}

void ftspi020_nor_cmdfunc(SPI_REG *base, unsigned int command, int addr)
{
	struct ftspi020_cmd *cmd = &cmd_global;
	int ret;

	switch (command) {
		case CMD_READ_ID:
			DEBUG_PRINT("ftspi020_nor: Read ID@(CE:%d)\r\n", select_chip);

			cmd->cq0 = 0;
			cmd->cq1 = FTSPI020_CMD1_OP_CODE_1_BYTE |
			           FTSPI020_CMD1_ADDR_BYTES(1);
			cmd->cq2 = 3;
			cmd->cq3 = FTSPI020_CMD3_INSTR_CODE(CMD_READ_ID) |
			           FTSPI020_CMD3_CE(select_chip) |
			           FTSPI020_CMD3_READ |
			           FTSPI020_CMD3_INTR_EN;

			ret = ftspi020_firecmd(base, cmd, NULL, nor_id, 3);
			if(ret)
			{
				DEBUG_PRINT("ftspi020_nor: error %d reading byte\r\n", (int)ret);
			}
			break;

		case CMD_ERASE_4K:
		case CMD_ERASE_32K:
		case CMD_ERASE_64K:
		case CMD_ERASE_CHIP:
			//DEBUG_PRINT("ftspi020_nor: Erase@(CE:%d) addr = 0x%x\r\n", select_chip, addr);
			ftspi020_nor_write_enable(base);

			cmd->cq0 = addr;
			cmd->cq1 = FTSPI020_CMD1_OP_CODE_1_BYTE |
	    	           FTSPI020_CMD1_ADDR_BYTES(3);
			cmd->cq2 = 0;
			cmd->cq3 = FTSPI020_CMD3_INSTR_CODE(command) |
	   	               FTSPI020_CMD3_CE(select_chip) |
	   	               FTSPI020_CMD3_WRITE |
	    	           FTSPI020_CMD3_INTR_EN;
			ret = ftspi020_firecmd(base, cmd, NULL, NULL, 8);
			if(ret)
			{
				DEBUG_PRINT("ftspi020_nor: error %d erasing block\r\n", (int)ret);
			}

			ftspi020_nor_wait_till_ready(base);
			break;
			/*
		case NAND_CMD_ERASE2:
		case NAND_CMD_PAGEPROG:
		case NAND_CMD_SEQIN:
		case NAND_CMD_STATUS:
			break;
			*/
		default:
			break;
	}
}

static unsigned char ftspi020_nor_program_code(void)
{
	switch (flash_read) {
	case SPI_NOR_QUAD:
		return CMD_QUAD_PAGE_PROGRAM;
	default:
		return CMD_PAGE_PROGRAM;
	}
}

static int ftspi020_nor_page_program(SPI_REG *base, int addr, int len, const u8 *buf)
{
	struct ftspi020_cmd *cmd = &cmd_global;
	unsigned char pagecode;
	int ret;

	cmd->cq0 = addr;
	cmd->cq1 = FTSPI020_CMD1_OP_CODE_1_BYTE |
	           FTSPI020_CMD1_ADDR_BYTES(3);
	cmd->cq2 = len;
	pagecode = ftspi020_nor_program_code();
	cmd->cq3 = FTSPI020_CMD3_INSTR_CODE(pagecode) |
	           FTSPI020_CMD3_CE(select_chip) |
	           ftspi020_nor_tx_nbits() |
	           FTSPI020_CMD3_WRITE |
	           FTSPI020_CMD3_INTR_EN;
#if CPU_MODE
	ret = ftspi020_firecmd(base, cmd, buf, NULL, 32);
	if(ret) {
		DEBUG_PRINT("ftspi020_nand: error %d loading to cache\r\n", (int)ret);

		return ret;
	}
#endif

#if DMA_MODE
	base->ICR = (1 << 8) | (1 << 0); //TXFIFO_THOD = 16, DMA_EN
	base->CMD0 = cmd->cq0;
	base->CMD1 = cmd->cq1;
	base->CMD2 = cmd->cq2;
	base->CMD3 = cmd->cq3;

	fLib_DMA0((UINT32)(&(base->DR)), (UINT32)buf, len, DMA_CHANEL_SPI0_FLASH, 4, RAM2FIFO);
	rt_sem_take(flash_rw_sem, RT_WAITING_FOREVER);

	base->ICR &= ~(1 << 0); //DMA_EN

	ret = ftspi020_wait_complete(base, 1000000);
	if (!ret) {
		fLib_printf("write command timeout %d\n", ret);
	}
#endif

	return 0;
}

void ftspi020_nor_write_buf(SPI_REG *base, const u8 *buf, int len, int addr)
{
	//DEBUG_PRINT("ftspi020_nor: write_buf ce = %d addr = 0x%x size = %d\r\n", select_chip, addr, len);

	if(ftspi020_nor_unlock(base)) {
		DEBUG_PRINT("ftspi020_nor: write_buf unlock failed...\r\n");
		return;
	}

	if(ftspi020_nor_write_enable(base)) {
		DEBUG_PRINT("ftspi020_nor: write_buf enable failed...\r\n");
		return;
	}

	if(ftspi020_nor_page_program(base, addr, len, buf)) {
		DEBUG_PRINT("ftspi020_nor: write_buf load failed...\r\n");
		return;
	}

	/* Wait until finished previous write command. */
	if(ftspi020_nor_wait_till_ready(base)) {
		DEBUG_PRINT("ftspi020_nor: write_buf wait failed...\r\n");
		return;
	}

	return;
}

static void flash_rw_done_callback(dma_event_t event, void  *user_data)
{
	if( event != DMA_EVENT_TC ){
		rt_kprintf("DMA error occurred!\n");
		return;
	}
	rt_sem_release(flash_rw_sem);
}

void SPI_FLASH_Init(void)
{
	SPI_REG *base = (SPI_REG *) SPI_BASE_ADDR[SPI0];
	ftspi020_nor_hw_setup(base);
	flash_rw_sem = rt_sem_create("flash_read", 0, RT_IPC_FLAG_FIFO);
	fLib_DMARegisterCallback(DMA_CHANEL_SPI0_FLASH, flash_rw_done_callback, NULL);
}

//the erase size needs to be 4KB aligned
UINT32 SPI_FLASH_BufferErase(u32 EraseAddr, u32 NumByteToErase)
{
	SPI_REG *base = (SPI_REG *) SPI_BASE_ADDR[SPI0];
	
	UINT32 erasecycle;
	UINT32 eraselen;

    if(NumByteToErase % ERASE_SIZE_4K){
		erasecycle = (NumByteToErase / ERASE_SIZE_4K) + 1;
	}else{
		erasecycle = NumByteToErase / ERASE_SIZE_4K;
	}
	
	eraselen = ERASE_SIZE_4K * erasecycle;
	
	while(erasecycle--){
		ftspi020_nor_cmdfunc(base, CMD_ERASE_4K, EraseAddr);
		EraseAddr += ERASE_SIZE_4K;
	}

	return eraselen;
}

//the write size needs to be 256 bytes aligned
UINT32 SPI_FLASH_BufferWrite(unsigned char* pBuffer, unsigned int WriteAddr, unsigned int NumByteToWrite)
{
	SPI_REG *base = (SPI_REG *) SPI_BASE_ADDR[SPI0];
	
	unsigned char *pageBuf;
    int remainingsize = NumByteToWrite % FLASH_PAGE_SIZE;
	int mainpart = NumByteToWrite - remainingsize;

	while(mainpart){
		ftspi020_nor_write_buf(base, pBuffer, FLASH_PAGE_SIZE, WriteAddr);
		pBuffer += FLASH_PAGE_SIZE;
		WriteAddr += FLASH_PAGE_SIZE;
		mainpart -= FLASH_PAGE_SIZE;
	};

	if(remainingsize){
		pageBuf = (unsigned char *)rt_malloc(FLASH_PAGE_SIZE);
		memset(pageBuf, 0, FLASH_PAGE_SIZE);
		memcpy(pageBuf, pBuffer, remainingsize);
		ftspi020_nor_write_buf(base, pageBuf, FLASH_PAGE_SIZE, WriteAddr);
		rt_free(pageBuf);
		return ((NumByteToWrite - remainingsize) + FLASH_PAGE_SIZE);
	}

	return NumByteToWrite;
}

UINT32 SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u32 NumByteToRead)
{		
	SPI_REG *base = (SPI_REG *) SPI_BASE_ADDR[SPI0];
	
	int ret;
	ret = ftspi020_nor_read_buf(base, pBuffer, NumByteToRead, ReadAddr);
    if(ret){
		return ret;
	}
	
	return NumByteToRead;
}

//this function is only for flash performance test, don't call it!
UINT32 SPI_FLASH_BufferRead_DMA(u8* pBuffer, u32 ReadAddr, u32 NumByteToRead)
{		
	SPI_REG *base = (SPI_REG *) SPI_BASE_ADDR[SPI0];

	UINT32 ret;

	base->ICR = (1 << 12) | (1 << 0); //RXFIFO_THOD = 16, DMA_EN
	
	base->CMD0 = ReadAddr;
	base->CMD1 = FTSPI020_CMD1_CONT_READ_MODE_DIS |
				 FTSPI020_CMD1_OP_CODE_1_BYTE |
	             FTSPI020_CMD1_DUMMY_CYCLE(6) |
	             FTSPI020_CMD1_ADDR_BYTES(3);
	base->CMD2 = NumByteToRead;
	base->CMD3 = FTSPI020_CMD3_INSTR_CODE(CMD_READ_QUAD_IO_FAST) |
				 FTSPI020_CMD3_CONT_READ_CODE(0xB0) |
	             FTSPI020_CMD3_CE(0) |
	             FTSPI020_CMD3_QUAD_IO_MODE |
	             FTSPI020_CMD3_READ |
	             FTSPI020_CMD3_INTR_EN;

	fLib_DMA0((UINT32)pBuffer, (UINT32)(&(base->DR)), NumByteToRead, DMA_CHANEL_SPI0_FLASH, 4, FIFO2RAM);
	rt_sem_take(flash_rw_sem, RT_WAITING_FOREVER);
	base->ICR &= ~(1 << 0); //RXFIFO_THOD = 16, DMA_EN
	
	ret = ftspi020_wait_complete(base, 1000000);
	if (!ret) {
		ret = -ETIMEDOUT;
		fLib_printf("flash read cmd timeout!\n");
		goto out;
	}

	return 0;
out:
	ftspi020_reset_ctrl(base);
	return ret;
}


